<template>
  <div class="upload-box">
    <el-upload v-model:file-list="fileList" :accept="fileType.join(',')" :action="updateUrl"
      :before-upload="beforeUpload" :class="['upload', drag ? 'no-border' : '']" :drag="drag" :headers="uploadHeaders"
      :limit="limit" :on-error="uploadError" :on-exceed="handleExceed" :on-success="uploadSuccess"
      list-type="picture-card">
      <div class="upload-empty">
        <slot name="empty">
          <Icon icon="ep:plus" />
          <!-- <span>请上传图片</span> -->
        </slot>
      </div>
      <template #file="{ file }">
        <img :src="file.url" class="upload-image" />
        <div class="upload-handle" @click.stop>
          <div class="handle-icon" @click="handlePictureCardPreview(file)">
            <Icon icon="ep:zoom-in" />
            <span>查看</span>
          </div>
          <div class="handle-icon" @click="handleRemove(file)">
            <Icon icon="ep:delete" />
            <span>删除</span>
          </div>
        </div>
      </template>
    </el-upload>
    <div class="el-upload__tip">
      <slot name="tip"></slot>
    </div>
    <el-image-viewer v-if="imgViewVisible" :url-list="[viewImageUrl]" @close="imgViewVisible = false" />
  </div>
</template>
<script lang="ts" name="UploadImgs" setup>
  import { PropType } from 'vue'
  import type { UploadFile, UploadProps, UploadUserFile } from 'element-plus'
  import { ElNotification } from 'element-plus'

  import { propTypes } from '@/utils/propTypes'
  import { getAccessToken, getTenantId } from '@/utils/auth'
  import { compressImg } from '@/utils/common'

  const message = useMessage() // 消息弹窗

  type FileTypes =
    | 'image/apng'
    | 'image/bmp'
    | 'image/gif'
    | 'image/jpeg'
    | 'image/pjpeg'
    | 'image/png'
    | 'image/svg+xml'
    | 'image/tiff'
    | 'image/webp'
    | 'image/x-icon'

  const props = defineProps({
    modelValue: {
      type: Array as PropType<UploadUserFile[]>,
      required: true
    },
    updateUrl: propTypes.string.def(import.meta.env.VITE_UPLOAD_URL),
    drag: propTypes.bool.def(true), // 是否支持拖拽上传 ==> 非必传（默认为 true）
    disabled: propTypes.bool.def(false), // 是否禁用上传组件 ==> 非必传（默认为 false）
    limit: propTypes.number.def(5), // 最大图片上传数 ==> 非必传（默认为 5张）
    fileSize: propTypes.number.def(10), // 图片大小限制 ==> 非必传（默认为 5M）
    fileType: propTypes.array.def(['image/jpeg', 'image/png', 'image/jpg']), // 图片类型限制 ==> 非必传（默认为 ["image/jpeg", "image/png", "image/gif"]）
    height: propTypes.string.def('150px'), // 组件高度 ==> 非必传（默认为 150px）
    width: propTypes.string.def('150px'), // 组件宽度 ==> 非必传（默认为 150px）
    borderRadius: propTypes.string.def('8px') // 组件边框圆角 ==> 非必传（默认为 8px）
  })

  const uploadHeaders = ref({
    Authorization: 'Bearer ' + getAccessToken(),
    'tenant-id': getTenantId()
  })

  const fileList = ref < UploadUserFile[] > ()
  // fix: 改为动态监听赋值解决图片回显问题
  watch(
    () => props.modelValue,
    (data) => {
      if (!data) return
      fileList.value = data
    },
    {
      deep: true,
      immediate: true
    }
  )
  /**
   * @description 文件上传之前判断
   * @param rawFile 上传的文件
   * */
  const beforeUpload: UploadProps['beforeUpload'] = (rawFile) => {
    const imgSize = rawFile.size / 1024 / 1024 < props.fileSize
    const imgType = props.fileType
    if (!imgType.includes(rawFile.type as FileTypes))
      ElNotification({
        title: '温馨提示',
        message: '上传图片不符合所需的格式！',
        type: 'warning'
      })

    const isTHAN1M = rawFile.size / 1024 > 1000
    if (isTHAN1M) {
      return compressImg(rawFile)
    } else {
      return imgType.includes(rawFile.type as FileTypes) && !isTHAN1M
    }
  }

  // 图片上传成功
  interface UploadEmits {
    (e: 'update:modelValue', value: UploadUserFile[]): void
  }

  const emit = defineEmits < UploadEmits > ()
  const uploadSuccess = (response, uploadFile: UploadFile) => {
    if (!response) return
    // TODO 多图上传组件成功后只是把保存成功后的url替换掉组件选图时的文件路径，所以返回的fileList包含的是一个包含文件信息的对象列表
    if (response.code === 0) {
      uploadFile.url = response.data.url
      emit('update:modelValue', fileList.value)
      message.success('上传成功')
    } else {
      message.error(response.msg)
    }
  }

  // 删除图片
  const handleRemove = (uploadFile: UploadFile) => {
    fileList.value = fileList.value.filter(
      (item) => item.url !== uploadFile.url || item.name !== uploadFile.name
    )
    emit('update:modelValue', fileList.value)
  }

  // 图片上传错误提示
  const uploadError = () => {
    ElNotification({
      title: '温馨提示',
      message: '图片上传失败，请您重新上传！',
      type: 'error'
    })
  }

  // 文件数超出提示
  const handleExceed = () => {
    ElNotification({
      title: '温馨提示',
      message: `当前最多只能上传 ${props.limit} 张图片，请移除后上传！`,
      type: 'warning'
    })
  }

  // 图片预览
  const viewImageUrl = ref('')
  const imgViewVisible = ref(false)
  const handlePictureCardPreview: UploadProps['onPreview'] = (uploadFile) => {
    viewImageUrl.value = uploadFile.url!
    imgViewVisible.value = true
  }
</script>

<style lang="scss" scoped>
  .is-error {
    .upload {

      :deep(.el-upload--picture-card),
      :deep(.el-upload-dragger) {
        border: 1px dashed var(--el-color-danger) !important;

        &:hover {
          border-color: var(--el-color-primary) !important;
        }
      }
    }
  }

  :deep(.disabled) {

    .el-upload--picture-card,
    .el-upload-dragger {
      cursor: not-allowed;
      background: var(--el-disabled-bg-color) !important;
      border: 1px dashed var(--el-border-color-darker);

      &:hover {
        border-color: var(--el-border-color-darker) !important;
      }
    }
  }

  .upload-box {
    .no-border {
      :deep(.el-upload--picture-card) {
        border: none !important;
      }
    }

    :deep(.upload) {
      .el-upload-dragger {
        display: flex;
        align-items: center;
        justify-content: center;
        width: 100%;
        height: 100%;
        padding: 0;
        overflow: hidden;
        border: 1px dashed var(--el-border-color-darker);
        border-radius: v-bind(borderRadius);

        &:hover {
          border: 1px dashed var(--el-color-primary);
        }
      }

      .el-upload-dragger.is-dragover {
        background-color: var(--el-color-primary-light-9);
        border: 2px dashed var(--el-color-primary) !important;
      }

      .el-upload-list__item,
      .el-upload--picture-card {
        width: v-bind(width);
        height: v-bind(height);
        background-color: transparent;
        border-radius: v-bind(borderRadius);
      }

      .upload-image {
        width: 100%;
        height: 100%;
        object-fit: contain;
      }

      .upload-handle {
        position: absolute;
        top: 0;
        right: 0;
        box-sizing: border-box;
        display: flex;
        align-items: center;
        justify-content: center;
        width: 100%;
        height: 100%;
        cursor: pointer;
        background: rgb(0 0 0 / 60%);
        opacity: 0;
        transition: var(--el-transition-duration-fast);

        .handle-icon {
          display: flex;
          flex-direction: column;
          align-items: center;
          justify-content: center;
          padding: 0 6%;
          color: aliceblue;

          .el-icon {
            margin-bottom: 15%;
            font-size: 140%;
          }

          span {
            font-size: 100%;
          }
        }
      }

      .el-upload-list__item {
        &:hover {
          .upload-handle {
            opacity: 1;
          }
        }
      }

      .upload-empty {
        display: flex;
        flex-direction: column;
        align-items: center;
        font-size: 12px;
        line-height: 30px;
        color: var(--el-color-info);

        .el-icon {
          font-size: 28px;
          color: var(--el-text-color-secondary);
        }
      }
    }

    .el-upload__tip {
      line-height: 15px;
      text-align: center;
    }
  }
</style>